#pragma once

#include "BigNumber.h"

class ByteBuffer
{
private:
	std::vector<uint8> mData;
	uint32 mReadPos;
public:
	ByteBuffer(const std::vector<uint8>& data) {
		mData.assign(data.begin(), data.end());
		mReadPos = 0;
	}

	ByteBuffer() {
		mReadPos = 0;
	}

	template<typename T>
	void set(const T& value, uint32 pos) {
		if(pos + sizeof(T) > mData.size())
			throw std::out_of_range(__FUNCTION__);

		memcpy(&mData[pos], &value, sizeof(T));
	}

	const std::vector<uint8>& getBuffer() const { return mData; }

	template<typename T>
	ByteBuffer& operator >> (T& value) {
		if(mReadPos + sizeof(T) > mData.size())
			throw std::out_of_range(__FUNCTION__);

		memcpy(&value, &mData[mReadPos], sizeof(T));
		mReadPos += sizeof(T);

		return *this;
	}

	template<typename T>
	ByteBuffer& operator << (const T& value) {
		mData.insert(mData.end(), (uint8*)&value, ((uint8*)(&value)) + sizeof(T));
		return *this;
	}

	template<>
	ByteBuffer& operator << <std::string> (const std::string& val) {
		mData.insert(mData.end(), val.begin(), val.end());

		mData.push_back(0);
		return *this;
	}

	template<>
	ByteBuffer& operator << <Crypt::BigNumber> (const Crypt::BigNumber& val) {
		auto beg = val.AsByteArray();
		mData.insert(mData.end(), beg, beg + val.GetNumBytes());
		return *this;
	}

	void setRead(uint32 pos) {
		if(mData.size() <= pos)
			throw std::out_of_range(__FUNCTION__);

		mReadPos = pos;
	}

	void readBytes(std::vector<uint8>& buffer) {
		if(mReadPos + buffer.size() > mData.size())
			throw std::out_of_range(__FUNCTION__);

		std::copy(mData.begin() + mReadPos, mData.begin() + mReadPos + buffer.size(), buffer.begin());
		mReadPos += buffer.size();
	}
};